import pandas as pd
import numpy as np
import gc


def INTPresent(df, colName):  # 计算变化率
    """
    同列增长百分比
    观察值得变化率与目标值变化率关系
    """
    NewName = "{}_by%".format(colName)
    df[NewName] = round(
        ((df.loc[:, colName] - df.loc[:, colName].shift(1)) / df.loc[:, colName]), 5
    )

    return df, NewName


def direction(df, colName):  # 数值转布尔
    """
    获取数值方向， x > 0 == 1, x < 0 == -1
    """
    NewName = "{}_Boll".format(colName)
    df[NewName] = df[colName].apply(lambda x: 1 if x > 0 else (-1 if x < 0 else np.nan))

    return df, NewName


def A_B(df, colA, colB):  # df['a'] - df['b']
    """
    表格中 A-B取差值
    观察值得变化与目标值关系
    """
    NewName = "{}-{}".format(colA, colB)
    df.loc[:, NewName] = df.loc[:, colA] - df.loc[:, colB]
    return df, NewName


def ChangeBoll(df, key):  # 变化率布尔值
    """
    生成自平移线
    """
    df, key = INTPresent(df, key)
    df, newname = direction(df, key)
    df = df.drop(key, axis=1)
    return df, newname


def MAsBuliderMAIN(df, KEYLIST, MA_LIST, shift=True):  # 生成各种趋势线 MA, MA_BOLL ,MA-MAIN()
    """
    - 将股票数据转为
    - df = pandas.DataFrame
    - KEYLIST = 键值
    - MA_LIST = MA线段的各个值
    """
    for key in KEYLIST:
        for d in MA_LIST:
            # 生成MA
            cat = key + "_ma_" + str(d)
            df.loc[:, cat] = df.loc[:, key].rolling(d).mean().round(2)
            if shift == True:
                df.loc[:, cat] = df.loc[:, cat].shift(1)
            # 生成均线布尔值
            df, NewName = A_B(df, cat, key)
            df, name2 = direction(df, NewName)
            df.loc[:, name2] = -df.loc[:, name2]  # 翻转数值
            df = df.drop(NewName, axis=1)
            # 生成MA boll值
            df, NewName = INTPresent(df, cat)
            # 生成MA变化率 boll值
            df, NewName = direction(df, NewName)

    # df = df.fillna(0)
    return df


def close_high(colName, df, colName2, boll=True):  # 计算开盘收盘均线差值
    """
    - colname 列1
    - df 数据
    - colname 列2
    - boll 是否计算布尔值
    - A - B值的计算，并且添加布尔值
    """
    Dnumber = colName.split("_")
    colN2 = colName2 + "_ma_" + Dnumber[-1]
    cat = Dnumber[0] + "-" + colName2 + "_ma_" + Dnumber[-1]
    df.loc[:, cat] = df.loc[:, colName] - df.loc[:, colN2]
    if boll == True:
        df, newname = direction(df, cat)
    # print(cat, ":", colName, ":", colN2)
    return df


def NewLines(namelist, df):  # 线名称判断
    """
    - 添加开盘-收盘
    - 最高-最低
    - namelist = 文件名称
    - df = df
    """
    for colName in namelist:
        if (
            "ma" in colName
            and "-" not in colName
            and "%" not in colName
            and "Boll" not in colName
            and "by" not in colName
        ):
            if "close" in colName:
                df = close_high(colName, df, "open", boll=True)

            if "high" in colName:
                df = close_high(colName, df, "low", boll=False)
    return df


def High_Low_Main(Ma_day_list, df, start):  # 函数计算移动平均线的差值，例如开盘-收盘...
    """
    + Ma_day_list, 移动平均线的数值列表
    + t， 分割点位置
    + df pd.DataFrame
    """

    def keysorter(MA_list, key="high-low_ma_"):  # 排序器，由于名字问题，无法通过数字顺序准确排列
        """
        MA_list = Wave.Wave()[0]
        key = key
        """
        dlist = MA_list.copy()
        dlist.sort()

        sorts = []
        for num in dlist:
            name = key + str(num)
            sorts.append(name)
        return sorts

    def High_Low_MA(
        df,
        keylist_key=[],
        keylist_MA=[],
        start=2,
        key="high-low_ma_",
        how="Sell",
    ):  # 通过循环判断查找顺序中的问题
        """
        keylist_key = keysorter(MA_LIST[0], 'high-low_ma_')
        key = dataframe columns start withs
        how = 'Sell' greater 'Buy' lower
        """
        df = df.drop_duplicates("date")
        dic = []
        for ind in df.index.tolist():
            temp = df.iloc[ind]
            count = 0  # 计算非0线的数量
            num = 0  # 计数器
            match_start = 0  # 计算开始值,到达后num计数
            for rang in range(len(keylist_key) - 1):
                if temp[keylist_MA[rang + 1]] > 0:
                    count += 1
            for rang in range(len(keylist_key) - 1):
                if how == "Sell" and temp[keylist_key[rang + 1]] != 0:
                    if temp[keylist_key[rang]] >= temp[keylist_key[rang + 1]]:
                        match_start += 1
                        if match_start >= start:
                            num += -1
                    else:
                        break
                elif how == "Buy" and temp[keylist_key[rang + 1]] != 0:
                    if temp[keylist_key[rang]] <= temp[keylist_key[rang + 1]]:
                        match_start += 1
                        if match_start >= start:
                            num += 1
                    else:
                        break
            tempd = temp[keylist_key].to_dict()
            tempd["date"] = temp.loc["date"]
            col = key + how
            if count != 0:
                tempd[col] = round(num / count, 3)
                # print(temp.loc["date"], key + how, num, count, round(num / count, 3))

            dic.append(tempd)
        dft = pd.DataFrame(dic)
        dft.drop_duplicates("date", keep="first", inplace=True)

        return dft, col

    # 循环计算
    def High_Low_Merge(
        df,
        ma_day_list,
        start=2,
        key="high-low_ma_",
        howl=["Sell", "Buy"],
        revers=True,
    ):
        """
        计算高低顺序，总程序
        """

        keylist_key = keysorter(ma_day_list, key)
        keylist_MA = keysorter(ma_day_list, "close_ma_")

        for how in howl:
            dft, col = High_Low_MA(
                df,
                key=key,
                keylist_key=keylist_key,
                keylist_MA=keylist_MA,
                how=how,
                start=start,
            )
            if dft.shape[0] > 0:
                dft = dft[["date", col]]
                if revers == True:
                    dft.loc[:, col] = -dft.loc[:, col]

                df = pd.merge(df, dft, how="left", on="date")
                del dft
                gc.collect()
        df = df.fillna(0)
        df[key + "Main"] = df[key + howl[0]] + df[key + howl[1]]
        # print(keylist_key)
        # dft.drop(labels=[])
        return df

    df = High_Low_Merge(
        df,
        Ma_day_list,
        start=start,
        key="high-low_ma_",
        howl=["Sell", "Buy"],
        revers=True,
    )
    gc.collect()

    df = High_Low_Merge(
        df,
        Ma_day_list,
        start=start,
        key="close-open_ma_",
        howl=["Sell", "Buy"],
        revers=True,
    )
    gc.collect()
    return df


def SortListbyType(col_lists):  # 按照类型排序
    """
    对符号进行排序
    """

    for _ in range(2):
        bolls = []
        present = []

        for col in col_lists:
            m = []
            if "%" in col:
                col_lists.remove(col)
                if "-" in col:
                    present.append(col)
                else:
                    m.append(col)
            elif "Boll" in col:
                col_lists.remove(col)
                if "-" in col:
                    bolls.append(col)
                else:
                    m.append(col)
            # bolls.sort()
            # present.sort()
            bolls.extend(m)

        col_lists.extend(bolls + present)

    return list(col_lists)


# 对布尔值进行加总，并且删除完成计算的值，注意不要把sell和buy也删了
def BollSams(
    df, keyls, main_key, line_Pares=3, Ma_Day_List=[3, 5, 8], sub_Key="Boll", drop=True
):
    for key in keyls:
        temp_list = []
        for colNumber in range(df.shape[0]):
            # dic = {}  # "date", df.date.iloc[colNumber]
            l = []
            count = 0
            for num in Ma_Day_List:
                cat = key + "_ma_" + str(num)
                if df[cat].iloc[colNumber] > 0:
                    count += 1

            for col in df.columns.to_list():
                if key in col and main_key in col:
                    if sub_Key:
                        if sub_Key in col:
                            l.append(df.loc[colNumber, col])
                            if drop == True:
                                df = df.drop(col, axis=1)
                    else:
                        l.append(df.loc[colNumber, col])
                        if drop == True:
                            df = df.drop(col, axis=1)
            if sub_Key:
                cat = key + main_key + sub_Key + "_sum"
            else:
                cat = key + main_key + "_sum"
            if count != 0:
                dic = {
                    "date": df.date.iloc[colNumber],
                    cat: round(sum(l) / count, 2),
                }
                temp_list.append(dic)
        _df = pd.DataFrame(temp_list)
        df = pd.merge(df, _df, on="date")
        # print(df[cat])
        return df


def BollFastSum(_df, keyls, main_key, Ma_Day_List=[3, 5, 8], sub_Key="Boll", drop=True):
    """
    df = pd.DataFrame
    main_key = 主筛选词汇
    sub_key = 辅助词汇
    """

    for key in keyls:
        temp_list = []
        for col in _df.columns.to_list():
            if key in col and main_key in col:
                if sub_Key:
                    if sub_Key in col:
                        temp = _df.loc[:, col]
                        temp_list.append(temp)
                        if drop == True:
                            _df = _df.drop(col, axis=1)
                else:
                    temp = _df.loc[:, col]
                    temp_list.append(temp)
                    if drop == True:
                        _df = _df.drop(col, axis=1)

        if sub_Key:
            _df[key + main_key + sub_Key + "_sum"] = np.sum(temp_list, axis=0)
        else:
            _df[key + main_key + "_sum"] = np.sum(temp_list, axis=0)
    return _df


def SellSignal(df, ma_day_list, startday=2, line_Pares=1):  # 抛售信号
    # namelist = SortListbyType(df.columns.tolist()) # 按照类型分类整理
    # df = df.loc[:, namelist] # 重新赋值表格
    namelist = df.columns.tolist()  # 替换原来的namlist
    df = NewLines(namelist, df)  # 绘制新的计算线，计算开盘收盘差值，最高最低差值
    Ma_day_l = ma_day_list  # 对 WAVES.WAVES(NDAY) 进行切片
    Ma_day_l.sort()  # 按照数值大小排列
    # for d in range(nday - start):  # 不知道为什么循环在下一级失效，只能拿出来了
    t = startday * line_Pares
    df = High_Low_Main(Ma_day_l, df, t)  # 计算差值之间的布尔值
    return df


def resoult(df, code, AvableCash):
    dlist = pd.to_datetime(df.loc[:, "date"]).tolist()
    years = int(str(dlist[-1] - dlist[0]).split(" ")[0]) / 365
    finalValue = df.iloc[-2]["TotalValues"]
    persent = round(((df.iloc[-2]["TotalValues"] / AvableCash) ** (1 / years) - 1), 5)
    incraceRate = df.iloc[-2]["TotalValues"] / AvableCash - 1
    maxmentChange = (
        df.loc[:, "TotalValues"].max() - df.loc[:, "TotalValues"].min()
    ) / AvableCash
    callBacks = df.loc[:, "TotalValues"].min() / AvableCash
    dic = {
        "code": "'" + str(code),
        "period/year": round(years, 2),
        "finalValue": round(finalValue, 2),
        "%_pre_year": round(persent, 5),
        "%_increase": round(incraceRate, 5),
        "Change_Rate": round(maxmentChange, 5),
        "Maxmut_Loss": round(callBacks, 5),
    }
    return dic


def Waves(n):
    """
    获取波浪天数均线
    n = 线的组数
    """
    Buy, Sell, Total = [], [], []
    main, side = 5, 3
    total = main + side
    Total.append(total)
    Buy.append(main)
    Sell.append(side)
    for i in range(n):
        buy = main * 3 + side * 2
        sell = main * 2 + side
        total = buy + sell
        main, side = buy, sell
        Buy.append(buy)
        Sell.append(sell)
        Total.append(total)

    DC = []

    DC.extend(Buy)
    DC.extend(Sell)
    DC.extend(Total)

    return DC


def fibonacci(r):
    r = r * 3
    lists = []
    a = 1
    b = 0
    for i in range(r):
        c = a + b
        b = a
        a = c
        print(a)
        lists.append(a)
